﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Canvas梦幻大树生长动画特效 - A5源码</title>

<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
(function ($) {

	var Vector = function (x, y) {

		this.x = x || 0;

		this.y = y || 0;

	};

	Vector.prototype = {

		add: function (v) {

			this.x += v.x;

			this.y += v.y;

			return this;

		},

		length: function () {

			return Math.sqrt(this.x * this.x + this.y * this.y);

		},

		rotate: function (theta) {

			var x = this.x;

			var y = this.y;

			this.x = Math.cos(theta) * this.x - Math.sin(theta) * this.y;

			this.y = Math.sin(theta) * this.x + Math.cos(theta) * this.y;

			//this.x = Math.cos(theta) * x - Math.sin(theta) * y;

			//this.y = Math.sin(theta) * x + Math.cos(theta) * y;

			return this;

		},

		mult: function (f) {

			this.x *= f;

			this.y *= f;

			return this;

		}

	};



	var Leaf = function (p, r, c, ctx) {

		this.p = p || null;

		this.r = r || 0;

		this.c = c || 'rgba(255,255,255,1.0)';

		this.ctx = ctx;

	}



	Leaf.prototype = {

		render: function () {

			var that = this;

			var ctx = this.ctx;

			var f = Branch.random(1, 2)

			for (var i = 0; i < 5; i++) {

				(function (r) {

					setTimeout(function () {

						ctx.beginPath();

						ctx.fillStyle = that.color;

						ctx.moveTo(that.p.x, that.p.y);

						ctx.arc(that.p.x, that.p.y, r, 0, Branch.circle, true);

						ctx.fill();

					}, r * 60);

				})(i);

			}

		}

	}





	var Branch = function (p, v, r, c, t) {

		this.p = p || null;

		this.v = v || null;

		this.r = r || 0;

		this.length = 0;

		this.generation = 1;

		this.tree = t || null;

		this.color = c || 'rgba(255,255,255,1.0)';

		this.register();

	};



	Branch.prototype = {

		register: function () {

			this.tree.addBranch(this);

		},

		draw: function () {

			var ctx = this.tree.ctx;

			ctx.beginPath();

			ctx.fillStyle = this.color;

			ctx.moveTo(this.p.x, this.p.y);

			ctx.arc(this.p.x, this.p.y, this.r, 0, Branch.circle, true);

			ctx.fill();

		},

		modify: function () {

			var angle = 0.18 - (0.10 / this.generation);

			this.p.add(this.v);

			this.length += this.v.length();

			this.r *= 0.99;

			this.v.rotate(Branch.random(-angle, angle)); //.mult(0.996);

			if (this.r < 0.8 || this.generation > 10) {

				this.tree.removeBranch(this);

				var l = new Leaf(this.p, 10, this.color, this.tree.ctx);

				l.render();

			}

		},

		grow: function () {

			this.draw();

			this.modify();

			this.fork();

		},

		fork: function () {

			var p = this.length - Branch.random(100, 200); // + (this.generation * 10);

			if (p > 0) {

				var n = Math.round(Branch.random(1, 3));

				this.tree.stat.fork += n - 1;

				for (var i = 0; i < n; i++) {

					Branch.clone(this);

				}

				this.tree.removeBranch(this);

			}

		}

	};



	Branch.circle = 2 * Math.PI;

	Branch.random = function (min, max) {

		return Math.random() * (max - min) + min;

	};

	Branch.clone = function (b) {

		var r = new Branch(new Vector(b.p.x, b.p.y), new Vector(b.v.x, b.v.y), b.r, b.color, b.tree);

		r.generation = b.generation + 1;

		return r;

	};

	Branch.rgba = function (r, g, b, a) {

		return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';

	};

	Branch.randomrgba = function (min, max, a) {

		return Branch.rgba(Math.round(Branch.random(min, max)), Math.round(Branch.random(min, max)), Math.round(Branch.random(min, max)), a);

	};



	var Tree = function () {

		var branches = [];

		var timer;

		this.stat = {

			fork: 0,

			length: 0

		};

		this.addBranch = function (b) {

			branches.push(b);

		};

		this.removeBranch = function (b) {

			for (var i = 0; i < branches.length; i++) {

				if (branches[i] === b) {

					branches.splice(i, 1);

					return;

				}

			}

		};

		this.render = function (fn) {

			var that = this;

			timer = setInterval(function () {

				fn.apply(that, arguments);

				if (branches.length > 0) {

					for (var i = 0; i < branches.length; i++) {

						branches[i].grow();

					}

				}

				else {

					//clearInterval(timer);

				}

			}, 1000 / 30);

		};

		this.init = function (ctx) {

			this.ctx = ctx;

		};

		this.abort = function () {

			branches = [];

			this.stat = {

				fork: 0,

				length: 0

			}

		};

	};





	function init() {



		// init



		var $window = $(window);

		var $body = $("body");

		var canvas_width = $window.width();

		var canvas_height = $window.height() - 30;

		var center_x = canvas_width / 2;

		var stretch_factor = 600 / canvas_height;

		var y_speed = 3 / stretch_factor;

		var $statMsg = $("#statMsg");



		// tx



		var canvas = $('#canvas')[0];

		canvas.width = canvas_width;

		canvas.height = canvas_height;

		var ctx = canvas.getContext("2d");

		ctx.globalCompositeOperation = "lighter";



		// tree



		var t = new Tree();

		t.init(ctx);

		for (var i = 0; i < 3; i++) {

			new Branch(new Vector(center_x, canvas_height), new Vector(Math.random(-1, 1), -y_speed), 15 / stretch_factor, Branch.randomrgba(0, 255, 0.3), t);

		}

		t.render(function () {

			$statMsg.html(this.stat.fork);

		});




	}





	$(function () {

		init();

	});





})(jQuery);

</script>

<style type="text/css">

body {

	padding: 0;

	margin: 0;

	overflow: hidden;

	background: #fff;

	font-family: Courier;

}
canvas {

	background-color: #000;

	cursor: pointer;

}
</style>

</head>
<body>

<canvas id='canvas'></canvas>

<div id="statMsg" style="display:none;">阶段</div>

<div style="text-align:center;margin:1px 0; font:normal 14px/24px 'MicroSoft YaHei';">
<p>适用浏览器：360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗. 不支持IE8及以下浏览器。</p>
<p>来源：<a href="http://down.admin5.com/" target="_blank">A5源码</a></p>
</div>
</body>
</html>
